package com.fastloan.mgt.application.repay.service.impl;

import com.fastloan.common.BasicServiceImpl;
import com.fastloan.common.model.CheckResult;
import com.fastloan.mgt.application.base.model.Application;
import com.fastloan.mgt.application.base.service.ApplicationService;
import com.fastloan.mgt.application.contract.enums.LoanModeEnum;
import com.fastloan.mgt.application.contract.model.ApplicationContract;
import com.fastloan.mgt.application.contract.service.impl.loanmode.LoanMode;
import com.fastloan.mgt.application.enums.AppEnum;
import com.fastloan.mgt.application.repay.dao.ZyApplicationRepaymentPlanMapper;
import com.fastloan.mgt.application.repay.model.ZyApplicationRepaymentPlan;
import com.fastloan.mgt.application.repay.model.vo.RepayExt;
import com.fastloan.mgt.application.repay.service.ZyApplicationRepaymentPlanService;
import com.fastloan.mgt.processcontrol.base.util.BeanFactoryUtil;
import com.fastloan.mgt.product.basics.model.Product;
import com.fastloan.mgt.product.basics.service.ProductService;
import com.fastloan.mgt.product.enums.ProductEnumSet;
import com.fastloan.safe.core.util.ManagerKit;
import com.fastloan.utils.DateTimeUtil;
import com.kawins.result.ResultEnum;
import com.kawins.result.ResultTo;
import com.kawins.supers.table.manager.DataTable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;

import static com.fastloan.mgt.application.enums.AppEnum.RepayStatus.NOPAY;

/**
 * @author gelon
 * createTime 2017/11/10
 */
@Service
public class ZyApplicationRepaymentPlanServiceImpl extends BasicServiceImpl<ZyApplicationRepaymentPlan> implements ZyApplicationRepaymentPlanService {

    @Autowired
    private ZyApplicationRepaymentPlanMapper zyApplicationRepaymentPlanMapper;

    @Autowired
    private ApplicationService applicationService;

    @Autowired
    private ProductService productService;

    @Override
    @DataTable(name = "queryByAppId")
    public List<RepayExt> queryByAppId(Map<String, Object> map) {
        List<RepayExt> repayExtList = zyApplicationRepaymentPlanMapper.queryByAppId(map);
        repayExtList.sort(Comparator.comparing(RepayExt::getRepaymentOverdueDate));
        repayExtList.stream()
                .filter(repayExt -> NOPAY.getCode().equals(repayExt.getRepaymentStatus()))
                .findFirst()
                .ifPresent(ext -> ext.setCurrentPay(true));
        return repayExtList;
    }

    @Override
    public CheckResult repayment(String applicationId){
        CheckResult loanModeCheckResult = choiceLoanMode(applicationId);
        if (!loanModeCheckResult.isCheckResult()){
            return loanModeCheckResult;
        }
        LoanMode loanMode = loanModeCheckResult.getData(LoanMode.class);
        CheckResult checkResult = loanMode.repayment(applicationId);
        if (!checkResult.isCheckResult()){
            log.warn(checkResult.getCheckMsg());
            return CheckResult.fail(checkResult.getCheckMsg());
        }
        return CheckResult.success();
    }

    private CheckResult choiceLoanMode(String applicationId){
        Application application = applicationService.selectByPrimaryKey(applicationId);
        Product product = productService.selectByPrimaryKey(application.getProductId());
        LoanModeEnum loanModeEnum = LoanModeEnum.getById(product.getFundingId());
        Map<String, LoanMode> loanModeMap = BeanFactoryUtil.getBeansOfType(LoanMode.class);
        Optional<LoanMode> loanMode = loanModeMap.values().stream()
                .filter(ryLoanMode -> loanModeEnum.equals(ryLoanMode.loanMode()))
                .findFirst();
        if (!loanMode.isPresent()){
            String errorMsg = "未知的放款方式,进件ID:" + applicationId;
            log.warn(errorMsg);
            return CheckResult.fail(errorMsg);
        }
        return CheckResult.success(loanMode.get());
    }

    /**
     * 生成还款计划
     */
    @Override
    public BigDecimal insertRepaymentPlan(Application tApplication, Product product, ApplicationContract applicationContract) {
        List<ZyApplicationRepaymentPlan> plans;
        BigDecimal totalNeedPay = BigDecimal.ZERO;

        if (product.getRepayment().equals(ProductEnumSet.RepayType.EQ_PRINCIPAL.getCode())) {
            plans = generatorEqPrincipal(applicationContract);
        } else if (product.getRepayment().equals(ProductEnumSet.RepayType.EQ_PRINCIPAL_INTEREST.getCode())) {
            plans = generatorEqPrincipalInterest(applicationContract);
        } else if (product.getRepayment().equals(ProductEnumSet.RepayType.PAY_INTEREST_DUE_PRINCIPAL.getCode())) {
            plans = generatorPayInterestDuePrincipal(applicationContract);
        } else if (product.getRepayment().equals(ProductEnumSet.RepayType.REPAYMENT_DUE.getCode())) {
            plans = generatoerRepaymentDue(applicationContract);
        } else {
            throw new RuntimeException("错误的还款类型> " + product.getRepayment());
        }

        for (ZyApplicationRepaymentPlan plan : plans) {
            zyApplicationRepaymentPlanMapper.insertSelective(plan);
            totalNeedPay = add(totalNeedPay, plan.getPlanPeriodPrincipal(), plan.getPlanPeriodInterest());
        }

        tApplication.setAppNeedPay(totalNeedPay);
        applicationService.updateByPrimaryKeySelective(tApplication);
        return totalNeedPay;
    }


    /**
     * 生成到期还本付息
     */
    public List<ZyApplicationRepaymentPlan> generatoerRepaymentDue(ApplicationContract applicationContract) {
        BigDecimal allInterest = applicationContract.getLoanAmount().multiply(applicationContract.getLoanMonthRate())
                .multiply(new BigDecimal(applicationContract.getCreditPeriod())).divide(new BigDecimal(100), 9, BigDecimal.ROUND_HALF_UP);
        ZyApplicationRepaymentPlan tApplicationRepaymentPlan = new ZyApplicationRepaymentPlan()
                .setAppId(applicationContract.getApplicationId())
                .setPlanPeriodInterest(allInterest)
                .setPlanPeriodPrincipal(applicationContract.getLoanAmount())
                .setRepaymentOverdueDate(getRepayDue(applicationContract, applicationContract.getCreditPeriod()))
                .setCreateTime(new Date())
                .setFactRepaymentAount(new BigDecimal(0))
                .setRepaymentStatus(NOPAY.getCode())
                .setCreateBy(ManagerKit.getUserId())
                .setOffsetAmount(add(allInterest, applicationContract.getLoanAmount()));
        List<ZyApplicationRepaymentPlan> list = new ArrayList<>();
        list.add(tApplicationRepaymentPlan);
        return list;
    }

    /**
     * 生成到期还本
     */
    public List<ZyApplicationRepaymentPlan> generatorPayInterestDuePrincipal(ApplicationContract applicationContract) {
        BigDecimal currInterest = applicationContract.getLoanAmount().multiply(applicationContract.getLoanMonthRate()
                .divide(new BigDecimal(100), 9, BigDecimal.ROUND_HALF_UP));
        List<ZyApplicationRepaymentPlan> plans = new ArrayList<>();
        for (int i = 1; i <= applicationContract.getCreditPeriod(); i++) {
            ZyApplicationRepaymentPlan tApplicationRepaymentPlan = new ZyApplicationRepaymentPlan()
                    .setAppId(applicationContract.getApplicationId())
                    .setPlanPeriodInterest(currInterest)
                    .setPlanPeriodPrincipal(new BigDecimal(0))
                    .setRepaymentOverdueDate(getRepayDue(applicationContract, i))
                    .setCreateTime(new Date())
                    .setFactRepaymentAount(new BigDecimal(0))
                    .setRepaymentStatus(NOPAY.getCode())
                    .setCreateBy(ManagerKit.getUserId())
                    .setOffsetAmount(add(new BigDecimal(0), currInterest));
            if (i == applicationContract.getCreditPeriod()) {
                tApplicationRepaymentPlan.setPlanPeriodPrincipal(applicationContract.getLoanAmount())
                        .setOffsetAmount(add(applicationContract.getLoanAmount(), currInterest));
            }
            plans.add(tApplicationRepaymentPlan);
        }
        return plans;
    }


    /**
     * 生成等额本金还款计划
     *
     * @return 还款计划
     */
    public List<ZyApplicationRepaymentPlan> generatorEqPrincipal(ApplicationContract applicationContract) {
        BigDecimal lastPeriodLeaveAmount = applicationContract.getLoanAmount(); //放款
        BigDecimal currRepay = lastPeriodLeaveAmount.divide(new BigDecimal(applicationContract.getCreditPeriod()), 2, BigDecimal.ROUND_HALF_UP);
        List<ZyApplicationRepaymentPlan> plans = new ArrayList<>();
        for (int i = 1; i <= applicationContract.getCreditPeriod(); i++) {
            BigDecimal currInterest = lastPeriodLeaveAmount.multiply(applicationContract.getLoanMonthRate().divide(new BigDecimal(100), 9, BigDecimal.ROUND_HALF_UP));
            ZyApplicationRepaymentPlan tApplicationRepaymentPlan = new ZyApplicationRepaymentPlan()
                    .setAppId(applicationContract.getApplicationId())
                    .setPlanPeriodInterest(currInterest)
                    .setPlanPeriodPrincipal(currRepay)
                    .setRepaymentOverdueDate(getRepayDue(applicationContract, i))
                    .setCreateTime(new Date())
                    .setFactRepaymentAount(new BigDecimal(0))
                    .setRepaymentStatus(NOPAY.getCode())
                    .setCreateBy(ManagerKit.getUserId())
                    .setOffsetAmount(add(currRepay, currInterest));
            lastPeriodLeaveAmount = lastPeriodLeaveAmount.subtract(currRepay);
            plans.add(tApplicationRepaymentPlan);
        }
        return plans;
    }

    /**
     * 生成等额本息还款计划
     */
    List<ZyApplicationRepaymentPlan> generatorEqPrincipalInterest(ApplicationContract applicationContract) {
        //周期还款额=贷款本金×[月利率×(1+月利率)^还款月数]÷[(1+月利率) ^ 还款月数-1]
        BigDecimal periodRate = applicationContract.getLoanMonthRate().divide(new BigDecimal(100), 9, BigDecimal.ROUND_HALF_UP);
        BigDecimal countRepay = (applicationContract.getLoanAmount().multiply(periodRate)
                .multiply(add(new BigDecimal(1), periodRate).pow(applicationContract.getCreditPeriod())))
                .divide(add(new BigDecimal(1), periodRate).pow(applicationContract.getCreditPeriod()).subtract(new BigDecimal(1)), 10, BigDecimal.ROUND_HALF_UP);
        countRepay = countRepay.setScale(2, BigDecimal.ROUND_HALF_UP);
        List<ZyApplicationRepaymentPlan> plans = new ArrayList<>();
        BigDecimal lastPeriodLeaveAmount = applicationContract.getLoanAmount(); //本次还款之前欠款 初次为本金额度
        for (int i = 1; i <= applicationContract.getCreditPeriod(); i++) {
            BigDecimal currInterest = lastPeriodLeaveAmount.multiply(periodRate); //本次还利息
            if (currInterest.compareTo(countRepay) > 0) {
                currInterest = countRepay;
            }
            BigDecimal currRepay = countRepay.subtract(currInterest); //本次还本金
            lastPeriodLeaveAmount = lastPeriodLeaveAmount.subtract(currRepay);
            ZyApplicationRepaymentPlan tApplicationRepaymentPlan = new ZyApplicationRepaymentPlan()
                    .setAppId(applicationContract.getApplicationId())
                    .setPlanPeriodInterest(currInterest)
                    .setPlanPeriodPrincipal(currRepay)
                    .setRepaymentOverdueDate(getRepayDue(applicationContract, i))
                    .setCreateTime(new Date())
                    .setRepaymentStatus(NOPAY.getCode())
                    .setCreateBy(ManagerKit.getUserId())
                    .setFactRepaymentAount(new BigDecimal(0))
                    .setOffsetAmount(add(currRepay, currInterest));
            lastPeriodLeaveAmount = lastPeriodLeaveAmount.setScale(2, BigDecimal.ROUND_HALF_UP);
            plans.add(tApplicationRepaymentPlan);
        }
        return plans;
    }


    private BigDecimal add(BigDecimal... decimals) {
        BigDecimal curr = BigDecimal.ZERO;
        for (BigDecimal decimal : decimals) {
            curr = curr.add(decimal);
        }
        return curr;
    }


    private Date getRepayDue(ApplicationContract applicationContract, int lastDate) {
        return DateTimeUtil.addMonth(applicationContract.getLoanTime(), lastDate);
    }


//    public static void main(String... args){
//        ZyApplicationRepaymentPlanServiceImpl impl = new ZyApplicationRepaymentPlanServiceImpl();
//        Application application = new Application();
//        application.setLoanAmount(new BigDecimal(50000));
//        application.setLoanTime(new Date());
//        application.setApplicationId("123");
//
//        ApplicationContract applicationContract = new ApplicationContract();
//        applicationContract.setCreditPeriod(12);
//        applicationContract.setLoanMonthRate(new BigDecimal(5));
//        List<ZyApplicationRepaymentPlan> plans = impl.generatorEqPrincipal(application,applicationContract);
//        System.out.println("放款>>>" + application.getLoanAmount() + "         利息>>>" + applicationContract.getLoanMonthRate());
//        plans.stream().sequential().forEach(plan -> outPlan(plan));
//    }
//
//    private static void outPlan(ZyApplicationRepaymentPlan plan) {
//        StringBuffer sb = new StringBuffer();
//        sb.append("计划本金->")
//                .append(plan.getPlanPeriodPrincipal())
//                .append("---计划利息->")
//                .append(plan.getPlanPeriodInterest());
//        System.out.println(sb.toString());
//    }


}
